package com.ll19.webapp.controller;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.ll19.Constants;
import com.ll19.model.User;
import com.ll19.service.RoleManager;
import com.ll19.service.UserExistsException;
import com.ll19.webapp.util.RequestUtil;

/**
 * Controller to signup new users.
 * 
 * @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
 */

// @Controller：用于标识是处理器类；
// @RequestMapping：请求到处理器功能方法的映射规则；
// @RequestParam：请求参数到处理器功能处理方法的方法参数上的绑定；
// @ModelAttribute：请求参数到命令对象的绑定；
// @SessionAttributes：用于声明session级别存储的属性，放置在处理器类上，通常列出模型属性（如@ModelAttribute）对应的名称，则这些属性会透明的保存到session中；
// @InitBinder：自定义数据绑定注册支持，用于将请求参数转换到命令对象属性的对应类型；
//
// 注意：需要通过处理器映射DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter来开启支持@Controller
// 和 @RequestMapping注解的处理器。
// Spring3.0引入RESTful架构风格支持(通过@PathVariable注解和一些其他特性支持),且又引入了更多的注解支持
// @CookieValue：cookie数据到处理器功能处理方法的方法参数上的绑定；
// @RequestHeader：请求头（header）数据到处理器功能处理方法的方法参数上的绑定；
// @RequestBody：请求的body体的绑定（通过HttpMessageConverter进行类型转换）；
// @ResponseBody：处理器功能处理方法的返回值作为响应体（通过HttpMessageConverter进行类型转换）；
// @ResponseStatus：定义处理器功能处理方法/异常处理器返回的状态码和原因；
// @ExceptionHandler：注解式声明异常处理器；
// @PathVariable：请求URI中的模板变量部分到处理器功能处理方法的方法参数上的绑定，从而支持RESTful架构风格的URI；
// Spring3.1使用新的HandlerMapping 和
// HandlerAdapter来支持@Contoller和@RequestMapping注解处理器
// ，使用处理器映射RequestMappingHandlerMapping 和
// 处理器适配器RequestMappingHandlerAdapter组合来代替Spring2.5开始的处理器映射
// DefaultAnnotationHandlerMapping和处理器适配器AnnotationMethodHandlerAdapter。

// 请求头数据中有指定参数名
// 例子1：@RequestMapping(value=“/header/test1”, headers = “Accept”)：
// 表示请求的URL必须为“/header/test1”且 请求头中必须有Accept参数才能匹配。

// 请求头数据中没有指定参数名
// 例子：RequestMapping(value=“/header/test2”, headers = “!abc”)
// 表示请求的URL必须为“/header/test2” 且 请求头中必须没有abc参数才能匹配。（将Modify Header的abc参数值删除即可）

@Controller
// @RequestMapping(value={"/test1", "/user/create"})：多个URL路径可以映射到同一个处理器的功能处理方法。
@RequestMapping("/signup*")
public class SignupController extends BaseFormController {
	private RoleManager roleManager;

	@Autowired
	public void setRoleManager(RoleManager roleManager) {
		this.roleManager = roleManager;
	}

	public SignupController() {
		setCancelView("redirect:login");
		setSuccessView("redirect:mainMenu");
	}

	@ModelAttribute
	// 相对于@RequestMapping("/signup*")的映射进行窄化 可以设置相对的二级路径 已经设置是get还是post
	// 比如设置 @RequestMapping(value = "/hello") 则访问 signup/hello可以访问到此地址
	@RequestMapping(method = RequestMethod.GET)
	public User showForm() {
		return new User();
	}

	@RequestMapping(method = RequestMethod.POST)
	public String onSubmit(User user, BindingResult errors,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (request.getParameter("cancel") != null) {
			return getCancelView();
		}

		if (validator != null) { // validator is null during testing
			validator.validate(user, errors);

			if (errors.hasErrors()) {
				return "signup";
			}
		}

		Locale locale = request.getLocale();

		user.setEnabled(true);

		// Set the default user role on this new user
		user.addRole(roleManager.getRole(Constants.USER_ROLE));

		try {
			this.getUserManager().saveUser(user);
		} catch (AccessDeniedException ade) {
			// thrown by UserSecurityAdvice configured in aop:advisor
			// userManagerSecurity
			log.warn(ade.getMessage());
			response.sendError(HttpServletResponse.SC_FORBIDDEN);
			return null;
		} catch (UserExistsException e) {
			errors.rejectValue("username", "errors.existing.user",
					new Object[] { user.getUsername(), user.getEmail() },
					"duplicate user");

			// redisplay the unencrypted passwords
			user.setPassword(user.getConfirmPassword());
			return "signup";
		}

		saveMessage(request,
				getText("user.registered", user.getUsername(), locale));
		request.getSession().setAttribute(Constants.REGISTERED, Boolean.TRUE);

		// log user in automatically
		UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(
				user.getUsername(), user.getConfirmPassword(),
				user.getAuthorities());
		auth.setDetails(user);
		SecurityContextHolder.getContext().setAuthentication(auth);

		// Send user an e-mail
		if (log.isDebugEnabled()) {
			log.debug("Sending user '" + user.getUsername()
					+ "' an account information e-mail");
		}

		// Send an account information e-mail
		message.setSubject(getText("signup.email.subject", locale));

		try {
			sendUserMessage(user, getText("signup.email.message", locale),
					RequestUtil.getAppURL(request));
		} catch (MailException me) {
			saveError(request, me.getMostSpecificCause().getMessage());
		}

		return getSuccessView();
	}
}
